package ccpv;

import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Utilities {

	/**
	 * Debugging function used to generator 100 normally distributed numbers to a file.
	 * @param mean
	 * @param stdDev
	 */
	public static void generateGaussian(double mean, double stdDev) {
		
		FileWriter outfile;
		try {
			outfile = new FileWriter("output.csv");
			Random generator = new Random();
			for (int i = 0; i<100; i++) {
		  	  outfile.write(Double.toString(normalRandom(generator, mean,stdDev)));
		  	  outfile.write("\r\n");
			}
		} catch (IOException e) {

			e.printStackTrace();
		}

	
	}
	

	/**
	 * Generates a random number with normal distribution using the supplied random generator, mean, and standard deviation.
	 * @param generator The random generator to be used.
	 * @param mean The mean of the normal distribution.
	 * @param stdDev The standard deviation of the normal distribution.
	 * @return A random number in the normal distribution specified.
	 */
	public static double normalRandom(Random generator, double mean, double stdDev) {
		return generator.nextGaussian()*stdDev+mean;	
	}

	/**
	 * Generates a random number with normal distribution using the supplied mean and standard deviation.
	 * @param mean The mean of the normal distribution.
	 * @param stdDev The standard deviation of the normal distribution.
	 * @return A random number in the normal distribution specified.
	 */
	public static double normalRandom(double mean, double stdDev) {
		Random generator = new Random();
		return generator.nextGaussian()*stdDev+mean;	
	}
	
	
	public void readXmlFile(String filename) {
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder;
        Document doc;
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse (new File(filename));
			doc.getDocumentElement().normalize();
			String root = doc.getDocumentElement().getNodeName();
			System.out.println("Processing file: "+filename+" with root: "+root+"\n");
			
			NodeList listOfPersons = doc.getElementsByTagName("person");
			loadSociety(doc);
			loadGeography(doc);
			loadDemographics(doc);
			loadInterventions(doc);
			loadIdeologies(doc);
			loadHistory(doc);
			loadRuntime(doc);
		}
		catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 catch (SAXException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
		} 
		 catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Loads the parameters that govern the society, such as level of democracy.
	 * @param doc
	 */
	public static void loadSociety(Document doc) {
		// load parameters that define the society, see Dr. Chai's ontology
	}
	
	/**
	 * Loads the geographical information including shapefiles. 
	 * @param doc
	 */
	public static void loadGeography(Document doc) {
		// load each shapefile
		// extract data on regions?
		// build a region for each data with 
	}
	
	/**
	 * Loads the ascriptive characteristics that will define the population as well as demographic information regarding the distribution and correlations between characteristics. Each region may have its own demographics. 
	 * @param doc
	 */
	public static void loadDemographics(Document doc) {
		// option, specify existing shapefile with demographic agents pre-built
		// for each region
		  // load ascriptive characteristics
		  // load population demographics
		  // Load Distribution of population
		  // A. load coarse population distribution [random placement within region]
		  // B. load refined population distribution [clustered placement at coordinates within radius mimicking city centers]
		// build agents with specified ascriptive characteristics
		// save shapefile with loaded agents
	}
	
	/**
	 * Loads the interventions that will be applied during the runtime of the simulation. Interventions can exist for both the regional context as well as the global context.  
	 * @param doc
	 */
	public static void loadInterventions(Document doc) {
		// this is still highly undefined, discuss this further in meetings
		// possibilities include varying societies' parameters in hopes of affecting decisions and ideologies chosen
		// goal is to indirectly control society
		
		
	}
	
	/**
	 * Loads the history of events where ideologies have been in conflict and cooperation in the past. 
	 * @param doc
	 */
	public static void loadHistory(Document doc) {
		// build a table of historical events
		// currently assuming that history will be shared experiences between ideologies, but this may change in the future pending group discussion
		// simplified version would assume we have at least the ideologies, and outcomes of the event.
		// this may mean that we say whether the group was better or worse off after the event
		// cooperation can be modeled by both groups having positive outcome
		// conflict may result in both groups having negative outcomes, or one having positive and the other being negative
	}
	
	/**
	 * Loads the ideologies (and beliefs therein) that the agents will use to make their decisions.
	 * @param doc
	 */
	public static void loadIdeologies(Document doc) {
		// for each ideology present, load the beliefs in each one
		// beliefs are still highly undefined, oy
	}
	
	/**
	 * Loads the runtime parameters that control how the run will perform. This may include time limits, equation variables, which equations are used, and so forth.
	 * @param doc
	 */
	public static void loadRuntime(Document doc) {
		
	}
	
	

	/**
	 * Creates a list of Persons based upon the restrictions laid out in the features passed. 
	 * @param features The nested arraylist represents possible values for each category. It is assumed that the values sum to 1.0. 
	 * @param num The number of people to be generated. 
	 * @return
	 */
	public static ArrayList<Person> makePeople(ArrayList<NamedArrayList<IndexedParameter>> features, int num) {
		ArrayList<Person> people = new ArrayList<Person>();
		Random rand1 = new Random();
		double xtemp = 0, xsum = 0;
int count = 0;
		for (int i = 0; i<num; i++) {
		 Person p1 = new Person();	
		 for (NamedArrayList<IndexedParameter> feature: features) {
		  xtemp = rand1.nextDouble();
		  xsum = 0;
		  count = 0;
		  for (IndexedParameter char1 : feature) {
			  count++;
			  xsum += char1.getValue();
			  if (xtemp <= xsum) {
				p1.addCharacteristic(count); 
				break;
			  }
		  }
		  
		 }
		 people.add(p1);
		}
		return people;
	}
	
	public static Point2D generateRandomInArea(Area area1) {
		Rectangle2D bounds1 = area1.getBounds2D();
		Random rand1 = new Random();
		int counter = 0;
		double x,y;
		do {
		  counter++;
	  	  x = rand1.nextDouble()*bounds1.getWidth()+bounds1.getX();
		  y = rand1.nextDouble()*bounds1.getHeight()+bounds1.getY();
		}
		while (!area1.contains(x, y));
		
		System.out.println("# trials: "+counter);
		
		return new Point2D.Double(x,y);
		
	}
	
	public static Area buildAreaFromPoints(ArrayList<Point2D> points) {
		
		Path2D.Double path1 = new Path2D.Double();
		
		if (!points.isEmpty()){
		  Iterator<Point2D> itr = points.iterator();
		  Point2D start = itr.next();
		  Point2D temp = null;
		  path1.moveTo(start.getX(), start.getY());
		    while (itr.hasNext()) {
		      temp = itr.next();
		      path1.lineTo(temp.getX(), temp.getY());
		    }
		  path1.lineTo(start.getX(), start.getY());
		}

		Area result = new Area(path1);
		path1.closePath();
		assert result.isPolygonal();
		return result;
		
	}
	
/*	public ArrayList<ParameterList> gatherStatistics(ArrayList<Person> people) {
		ArrayList<ParameterList> features = new ArrayList<ParameterList>();
		if (people.isEmpty() || people.get(0).getCharacter().isEmpty()){
			return features;
		}
		int count = 0;
		int total = people.size();
		
		ArrayList<Characteristic> chars1 = people.get(0).getCharacter();
		for (Characteristic ch: chars1) {
			features.add(new ParameterList(ch.category));
		}
		
		for (Person p1: people) {
			count = 0;
			for (Characteristic ch : p1.getCharacter()) {
				
				features.get(count).addUnique(new Parameter(ch.category));
				count++;
			}
		}
		
		for (ParameterList p1 : features) {
			for (Parameter p2 : p1) {
				p2.setValue(p2.getValue()/total);
			}
		}
		
		return features;
	}
	
	*/
	
	public static double calcExpectedRegret() {
		return 0.0;
	}


}
